----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    16:37:00 01/18/2010 
-- Design Name: 
-- Module Name:    ControlUnit - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

-- General package
use work.GeneralProperties.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity ControlUnit is
    Port ( memory_data          : in     STD_LOGIC_VECTOR ((bus_size-1) downto 0);
	        memory_data_out      : out     STD_LOGIC_VECTOR ((bus_size-1) downto 0);
           memory_read_address  : out    STD_LOGIC_VECTOR ((bus_size-1) downto 0);			  
           memory_write_address : out    STD_LOGIC_VECTOR ((bus_size-1) downto 0);
			  memory_read          : out    STD_LOGIC;
			  memory_write         : out    STD_LOGIC;			  
           CLOCK                : in     STD_LOGIC;
           RESET                : in     STD_LOGIC;
			  InImediate           : out    STD_LOGIC_VECTOR ((bus_size - 6) downto 0);
			  Flags                : in     STD_LOGIC_VECTOR (3 downto 0);
			  RegisterAValue       : in     STD_LOGIC_VECTOR ((bus_size - 1) downto 0);
			  RegisterBValue       : in     STD_LOGIC_VECTOR ((bus_size - 1) downto 0);
			  SelMuxInput          : out    MultiplexSignals;
           SelAluOp             : out    ALU_operations;
			  SelShifterOp         : out    Shifter_operations;
			  RegFileWriteAddress  : out    ProcessorRegisters;
			  RegFileReadAAddress  : out    ProcessorRegisters;
			  RegFileReadBAddress  : out    ProcessorRegisters;
			  RegFileReadAEnable   : out    STD_LOGIC;
			  RegFileReadBEnable   : out    STD_LOGIC;
			  WriteRegistersFile   : out    STD_LOGIC;
			  RunningStatus        : out    STD_LOGIC;
           OutputEnable         : out    STD_LOGIC
			  );
end ControlUnit;

architecture Behavioral of ControlUnit is

-- Program Counter
signal PC : std_logic_vector((bus_size-1) downto 0);

-- Control Unit state
signal CPU_STATE : ControlUnitStates;

begin    
  process (RESET,CLOCK)
  variable opcode                    : std_logic_vector(4 downto 0);
  variable IR                        : std_logic_vector((bus_size-1) downto 0);  
  variable RegPopUpdate              : ProcessorRegisters;
  variable LastOpFlags               : STD_LOGIC_VECTOR (3 downto 0);
  variable f_jump_relative           : std_logic;
  variable f_jump_relative_direction : std_logic;
  variable f_jump_cond_pass          : std_logic;
  variable f_jump_condition          : Conditions;
  variable f_jump_relative_step      : std_logic_vector((bus_size - 7) downto 0);
  variable wait_cycles               : integer := 0;
  begin
    if RESET = '1' then
	   PC <= (others => '0');
      RunningStatus <= '1';
		
		memory_read_address <= (others => '0');
		
		-- Control Word
		InImediate <= (others => '0');
		SelMuxInput <= sel_AluOut;
		SelAluOp <= op_pass;
		SelShifterOp <= op_pass;
		WriteRegistersFile <= '0';
		RegFileWriteAddress <= r0;
		RegFileReadAAddress <= r0;
		RegFileReadBAddress <= r0;
		RegFileReadAEnable <= '0';
		RegFileReadBEnable <= '0';
		OutputEnable <= '0';
		memory_write <= '0';
		memory_read <= '0';
		
		CPU_STATE <= fetch;
		
		wait_cycles := 0;
		f_jump_relative_direction := '0';
		f_jump_relative := '0';
		f_jump_relative_step := (others => '0');
		LastOpFlags := (others => '0');
		f_jump_condition := None;
	 else
	   if rising_edge(CLOCK) then
		  case CPU_STATE is

-------------------------------------------------------------------------------------------------------------------------------------------------------------------		    
			 when fetch =>			   			   
				memory_read_address <= PC;
				memory_read <= '1';
				
				-- Program Counter will be incremented at process exit
				PC <= PC + 1;
				
				-- Go to state decode...
				CPU_STATE <= decode;
			 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------			 
			 when decode =>			   
				-- Get Instruction
				IR := memory_data;
				
				memory_read <= '0';
				-- Most significant 5 bits holds the opcode
				opcode := IR((bus_size-1) downto ((bus_size-1)-4) );
				
				-- Now identify each instruction, prepare the operands and sets the execution proper state
				case opcode is
				  when i_mov =>
				    -- Decode operands
					 RegFileWriteAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadAAddress <= int2reg( IR( (bus_size-10) downto (bus_size-13) ) );
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_movi =>		
                -- Decode operands				  
					 SelMuxInput <= sel_Imediate; -- Select Mux to Imediate input
					 InImediate <= IR( (bus_size-6) downto 0 );
					 RegFileWriteAddress <= r1; -- Select Accumulator
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;					 
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_ldm =>
				    -- Load some value from memory and place in some register
					 RegFileWriteAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadAAddress <= int2reg( IR( (bus_size-10) downto (bus_size-13) ) );
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_Memory;
					 RegFileReadAEnable <= '1';					 					 
					 
					 CPU_STATE <= ex_ldm;
					 wait_cycles := 2;
				  
				  when i_stm =>
				    -- Store some value in memory from some register
				    RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= int2reg( IR( (bus_size-10) downto (bus_size-13) ) );
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '1';					 					 
					 
					 CPU_STATE <= ex_stm;
					 wait_cycles := 2;
					 
				  when i_and =>
				    -- Decode operands
					 RegFileWriteAddress <= r1;
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= int2reg( IR( (bus_size-10) downto (bus_size-13) ) );
					 SelAluOp <= op_and;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '1';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_or =>
				    -- Decode operands
					 RegFileWriteAddress <= r1;
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= int2reg( IR( (bus_size-10) downto (bus_size-13) ) );
					 SelAluOp <= op_or;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '1';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_not =>
				    -- Decode operands
					 RegFileWriteAddress <= r1;
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;
					 SelAluOp <= op_not;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '0';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_inc =>
				    -- Decode operands
					 RegFileWriteAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;
					 SelAluOp <= op_inc;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '0';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_dec =>
				    -- Decode operands
					 RegFileWriteAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;
					 SelAluOp <= op_dec;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '0';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_add =>
				    -- Decode operands
					 RegFileWriteAddress <= r1;
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= int2reg( IR( (bus_size-10) downto (bus_size-13) ) );
					 SelAluOp <= op_add;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '1';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				
				  when i_sub =>
				    -- Decode operands
					 RegFileWriteAddress <= r1;
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= int2reg( IR( (bus_size-10) downto (bus_size-13) ) );
					 SelAluOp <= op_sub;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '1';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_shfl =>
				    -- Decode operands
					 RegFileWriteAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_shift_left;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '0';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_shfr =>
				    -- Decode operands
					 RegFileWriteAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_shift_right;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '0';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_rotr =>
				    -- Decode operands
					 RegFileWriteAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_rotate_right;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '0';
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_jmp =>		
                -- Decode operands				  					 
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;					 
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;					 
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 					 
					 RegFileReadBEnable <= '0';
                
				
					 CPU_STATE <= ex_jmp;
					 f_jump_relative := '0';
					 f_jump_condition := None;
                wait_cycles := 1;					 
				  
				  when i_jmpr =>		
                -- Decode operands				  					 					 					 					 
					 CPU_STATE <= ex_jmp;
					 f_jump_relative := '1';
					 f_jump_relative_direction := IR( (bus_size-6) );
					 f_jump_relative_step := IR( (bus_size-7) downto 0 );
					 f_jump_condition := None;
                wait_cycles := 1;
				  
				  when i_jz =>		
                -- Decode operands				  					 
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;					 
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;					 
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 					 
					 RegFileReadBEnable <= '0';
                
				
					 CPU_STATE <= ex_jmp;
					 f_jump_relative := '0';
					 f_jump_condition := Zero;
                wait_cycles := 1;
				
				  when i_jzr =>		
                -- Decode operands				  					 					 					 					 
					 CPU_STATE <= ex_jmp;
					 f_jump_relative := '1';
					 f_jump_relative_direction := IR( (bus_size-6) );
					 f_jump_relative_step := IR( (bus_size-7) downto 0 );
					 f_jump_condition := Zero;
                wait_cycles := 1;
				
				  when i_jnz =>		
                -- Decode operands				  					 
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;					 
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;					 
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 					 
					 RegFileReadBEnable <= '0';
                
				
					 CPU_STATE <= ex_jmp;
					 f_jump_relative := '0';
					 f_jump_condition := NotZero;
                wait_cycles := 1;
					 
				  when i_jnzr =>		
                -- Decode operands				  					 					 					 					 
					 CPU_STATE <= ex_jmp;
					 f_jump_relative := '1';
					 f_jump_relative_direction := IR( (bus_size-6) );
					 f_jump_relative_step := IR( (bus_size-7) downto 0 );
					 f_jump_condition := NotZero;
                wait_cycles := 1;
				
				  when i_jp =>		
                -- Decode operands				  					 
					 RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;					 
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;					 
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 					 
					 RegFileReadBEnable <= '0';
                
				
					 CPU_STATE <= ex_jmp;
					 f_jump_relative := '0';
					 f_jump_condition := Sign;
                wait_cycles := 1;
				  
				  when i_jpr =>		
                -- Decode operands				  					 					 					 					 
					 CPU_STATE <= ex_jmp;
					 f_jump_relative := '1';
					 f_jump_relative_direction := IR( (bus_size-6) );
					 f_jump_relative_step := IR( (bus_size-7) downto 0 );
					 f_jump_condition := Sign;
                wait_cycles := 1;
				  
				  when i_call =>		
                -- Decode operands				  					 
					 RegFileReadAAddress <= r13;
					 RegFileWriteAddress <= r13;
					 RegFileReadBAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );					 
					 SelAluOp <= op_inc;
					 SelShifterOp <= op_pass;					 
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 					 
					 RegFileReadBEnable <= '1';
					 
					 CPU_STATE <= ex_call;					 
                wait_cycles := 3;
				  
				  when i_ret =>		
                -- Decode operands				  					 
					 RegFileReadAAddress <= r13;
					 RegFileWriteAddress <= r13;					 
					 SelAluOp <= op_dec;
					 SelShifterOp <= op_pass;					 
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 					 
					 RegFileReadBEnable <= '0';
					 
					 CPU_STATE <= ex_pop;					 
                wait_cycles := 3;
				  
				  when i_out =>
				    -- Store some value in memory from some register
				    RegFileReadAAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '0';					 					 
					 
					 CPU_STATE <= ex_out;
					 wait_cycles := 1;
				  
				  when i_halt =>
				    -- Halts forever
				    RegFileReadAAddress <= r0;
					 RegFileReadBAddress <= r0;
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_AluOut;
					 RegFileReadAEnable <= '0';					 					 
					 RegFileReadBEnable <= '0';					 					 
					 OutputEnable <= '0';
					 RunningStatus <= '0';
					 
					 --CPU_STATE <= ex_out; Stay here forever
					 wait_cycles := 0;
				  
				  when i_nop =>
				    -- Does nothing just eat a cycle				    					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 0;
				  
				  when i_in =>
				    -- Store some value in memory from some register
				    RegFileWriteAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadBAddress <= r0;
					 SelAluOp <= op_pass;
					 SelShifterOp <= op_pass;
					 SelMuxInput <= sel_External;
					 RegFileReadAEnable <= '0';					 					 
					 RegFileReadBEnable <= '0';					 					 
					 
					 CPU_STATE <= ex_mov;
					 wait_cycles := 1;
				  
				  when i_push =>
				    -- Save value in memory at address r13 and increment r13 value
				    RegFileReadAAddress <= r13;
					 RegFileWriteAddress <= r13;
					 RegFileReadBAddress <= int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '1';					 					 
					 SelMuxInput <= sel_AluOut;
					 SelAluOp <= op_inc;
					 SelShifterOp <= op_pass;
					 
					 CPU_STATE <= ex_push;
					 wait_cycles := 2;
				
				  when i_pop =>
				    -- Load value in memory at address r13 and decrement r13 value
				    RegFileReadAAddress <= r13;
					 RegFileWriteAddress <= r13;
					 RegPopUpdate := int2reg( IR( (bus_size-6) downto (bus_size-9) ) );
					 RegFileReadAEnable <= '1';					 					 
					 RegFileReadBEnable <= '0';					 					 					 
					 SelMuxInput <= sel_AluOut;
					 SelAluOp <= op_dec;
					 SelShifterOp <= op_pass;
					 
					 CPU_STATE <= ex_pop;
					 wait_cycles := 3;
					 
				  when others =>
				    null;
				end case;			   
			 
-------------------------------------------------------------------------------------------------------------------------------------------------------------------			 
			 when ex_mov =>			   				
				if wait_cycles = 0 then
				  CPU_STATE <= fetch;		
				  InImediate <= (others => '0');
		        SelMuxInput <= sel_AluOut;
		        SelAluOp <= op_pass;
		        SelShifterOp <= op_pass;
		        WriteRegistersFile <= '0';
		        RegFileWriteAddress <= r0;
		        RegFileReadAAddress <= r0;
		        RegFileReadBAddress <= r0;
		        RegFileReadAEnable <= '0';
		        RegFileReadBEnable <= '0';
		        OutputEnable <= '0';
				else
				  wait_cycles := wait_cycles - 1;
				  WriteRegistersFile <= '1';
				  LastOpFlags := Flags;
				end if;
			
			 when ex_jmp =>			   				
				if wait_cycles = 0 then
				  CPU_STATE <= fetch;		
				  InImediate <= (others => '0');
		        SelMuxInput <= sel_AluOut;
		        SelAluOp <= op_pass;
		        SelShifterOp <= op_pass;
		        WriteRegistersFile <= '0';
		        RegFileWriteAddress <= r0;
		        RegFileReadAAddress <= r0;
		        RegFileReadBAddress <= r0;
		        RegFileReadAEnable <= '0';
		        RegFileReadBEnable <= '0';
		        OutputEnable <= '0';
				  
				  f_jump_relative := '0';
				  f_jump_relative_direction := '0';
				  f_jump_relative_step := (others => '0');
				  LastOpFlags := (others => '0');
				  f_jump_condition := None;
				else
				  wait_cycles := wait_cycles - 1; 
				  
				  -- Process Jump conditions based on the last flags value
				  -- Flags <= ALUFlagCarry & ALUFlagZero & ALUFlagSign & ALUFlagOverflow;
				  f_jump_cond_pass := '0';
				  case f_jump_condition is
				    when Zero =>
					   if LastOpFlags(2) = '1' then
						  f_jump_cond_pass := '1';
						end if;
					 when NotZero =>
					   if LastOpFlags(2) = '0' then
						  f_jump_cond_pass := '1';
						end if;
					 when Sign =>
					   if LastOpFlags(1) = '1' then
						  f_jump_cond_pass := '1';
						end if;
					 when None =>
					   f_jump_cond_pass := '1';
				  end case;
				  
				  if f_jump_cond_pass = '1' then				  
				    if (f_jump_relative = '0') then
				      PC <= RegisterAValue;				    
				    else
				      -- Relative Jump
					   if f_jump_relative_direction = '0' then
					     -- Jump down
						  PC <= PC + ("000000" & (f_jump_relative_step - 1));
					   else
					     -- Jump up
						  PC <= PC - ("000000" & (f_jump_relative_step + 1));
					   end if;				    
                end if;				  
				  end if; 
				  
				end if;
			
			 when ex_ldm =>
			   if wait_cycles = 0 then
				  CPU_STATE <= fetch;		
				  InImediate <= (others => '0');
		        SelMuxInput <= sel_AluOut;
		        SelAluOp <= op_pass;
		        SelShifterOp <= op_pass;
		        WriteRegistersFile <= '0';
		        RegFileWriteAddress <= r0;
		        RegFileReadAAddress <= r0;
		        RegFileReadBAddress <= r0;
		        RegFileReadAEnable <= '0';
		        RegFileReadBEnable <= '0';				  
		        OutputEnable <= '0';
		        
		        memory_read <= '0';				  
				elsif wait_cycles = 1 then
				  WriteRegistersFile <= '1';

				elsif wait_cycles = 2 then
				  memory_read_address <= RegisterAValue;
				  memory_read <= '1';
				  LastOpFlags := Flags;
				end if;
				
				wait_cycles := wait_cycles - 1;
			 
			 when ex_stm =>
			   if wait_cycles = 0 then
				  CPU_STATE <= fetch;		
				  InImediate <= (others => '0');
		        SelMuxInput <= sel_AluOut;
		        SelAluOp <= op_pass;
		        SelShifterOp <= op_pass;
		        WriteRegistersFile <= '0';
		        RegFileWriteAddress <= r0;
		        RegFileReadAAddress <= r0;
		        RegFileReadBAddress <= r0;
		        RegFileReadAEnable <= '0';
		        RegFileReadBEnable <= '0';
		        OutputEnable <= '0';				  
				  memory_write <= '0';				  
				elsif wait_cycles = 1 then
				  memory_write <= '1';				  
				elsif wait_cycles = 2 then
				  memory_write_address <= RegisterAValue;
				  memory_data_out <= RegisterBValue;
              LastOpFlags := Flags;				  
				end if;
			   
				wait_cycles := wait_cycles - 1;
			 
			 when ex_push =>
			   if wait_cycles = 0 then
				  CPU_STATE <= fetch;		
				  InImediate <= (others => '0');
		        SelMuxInput <= sel_AluOut;
		        SelAluOp <= op_pass;
		        SelShifterOp <= op_pass;
		        WriteRegistersFile <= '0';
		        RegFileWriteAddress <= r0;
		        RegFileReadAAddress <= r0;
		        RegFileReadBAddress <= r0;
		        RegFileReadAEnable <= '0';
		        RegFileReadBEnable <= '0';
		        OutputEnable <= '0';				  
				  memory_write <= '0';				  
				elsif wait_cycles = 1 then
				  memory_write <= '1';
              WriteRegistersFile <= '1';				  
				elsif wait_cycles = 2 then
				  memory_write_address <= RegisterAValue;				  
				  memory_data_out <= RegisterBValue;
              LastOpFlags := Flags;				  
				end if;
			   
				wait_cycles := wait_cycles - 1;
		    
			 when ex_call =>
			   if wait_cycles = 0 then
				  CPU_STATE <= fetch;		
				  InImediate <= (others => '0');
		        SelMuxInput <= sel_AluOut;
		        SelAluOp <= op_pass;
		        SelShifterOp <= op_pass;
		        WriteRegistersFile <= '0';
		        RegFileWriteAddress <= r0;
		        RegFileReadAAddress <= r0;
		        RegFileReadBAddress <= r0;
		        RegFileReadAEnable <= '0';
		        RegFileReadBEnable <= '0';
		        OutputEnable <= '0';				  
				  memory_write <= '0';				  
				elsif wait_cycles = 1 then
				  -- Prepare for jump
				  PC <= RegisterBValue;				
				elsif wait_cycles = 2 then
				  memory_write <= '1';
              WriteRegistersFile <= '1';				    
            elsif wait_cycles = 3 then              
				  -- Save PC in stack
				  memory_write_address <= RegisterAValue;				  
				  memory_data_out <= PC;
              LastOpFlags := Flags;	
				end if;

            wait_cycles := wait_cycles - 1;				
				
			 when ex_pop =>
			   if wait_cycles = 0 then
				  CPU_STATE <= fetch;		
				  InImediate <= (others => '0');
		        SelMuxInput <= sel_AluOut;
		        SelAluOp <= op_pass;
		        SelShifterOp <= op_pass;
		        WriteRegistersFile <= '0';
		        RegFileWriteAddress <= r0;
		        RegFileReadAAddress <= r0;
		        RegFileReadBAddress <= r0;
		        RegFileReadAEnable <= '0';
		        RegFileReadBEnable <= '0';
		        OutputEnable <= '0';				  
				  memory_write <= '0';				  
				elsif wait_cycles = 1 then				  
              if opcode = i_ret then
				    PC <= memory_data;
				  else
				    WriteRegistersFile <= '1';				  
				  end if;				  
				elsif wait_cycles = 2 then
				  memory_read_address <= RegisterAValue;
				  memory_read <= '1';
				  
				  -- Specific for pop
				  if opcode = i_pop then
				    RegFileWriteAddress <= RegPopUpdate;					 
					 SelMuxInput <= sel_Memory;
				  end if;
				  
				  WriteRegistersFile <= '0';
              LastOpFlags := Flags;
            elsif wait_cycles = 3 then
				  -- Decrement r13
				  WriteRegistersFile <= '1';
				end if;
			   
				wait_cycles := wait_cycles - 1;
										
			 when ex_out =>
			   if wait_cycles = 0 then
				  CPU_STATE <= fetch;		
				  InImediate <= (others => '0');
		        SelMuxInput <= sel_AluOut;
		        SelAluOp <= op_pass;
		        SelShifterOp <= op_pass;
		        WriteRegistersFile <= '0';
		        RegFileWriteAddress <= r0;
		        RegFileReadAAddress <= r0;
		        RegFileReadBAddress <= r0;
		        RegFileReadAEnable <= '0';
		        RegFileReadBEnable <= '0';
		        OutputEnable <= '0';				  
				elsif wait_cycles = 1 then
				  OutputEnable <= '1';
              LastOpFlags := Flags;				  
				end if;
			   
				wait_cycles := wait_cycles - 1;
				
			 when others =>
			   null;
		  end case;
		end if;
	 end if;
  end process;

end Behavioral;

